#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <asm/uaccess.h>

#include "registers.h"

typedef unsigned int (*movefrom_func)(void);
typedef void (*moveto_func)(unsigned int value);

extern unsigned mfspr_table_func(void);
extern unsigned mfdcr_table_func(void);

int cpuregs_open(struct inode *inode, struct file *filp)
{
	/* Success */
	printk("cpuregs_open\n");
	return 0;
}

int cpuregs_release(struct inode *inode, struct file *filp)
{
	/* Success */
	printk("cpuregs_release\n");
	return 0;
}

static int cpuregs_ioctl(struct inode *inode, struct file *filp,
			unsigned int cmd, unsigned long arg)
{
	movefrom_func func;

	printk("cpuregs_ioctl\n");

	if (cmd & GET_SPR) {
		cmd = cmd & 0x3ff;
		printk("cmd = 0x%08x\n", cmd);

		func = mfspr_table_func + 8 * cmd;
		return func();
	} else if (cmd & GET_MSR) {
		return get_msr();
	} else if (cmd & GET_DCR) {
		cmd = cmd & 0x3ff;
		printk("cmd = 0x%08x\n", cmd);

		func = mfdcr_table_func + 8 * cmd;
		return func();
	} else {
		printk("Only read of SPRs is allowed for now\n");
		return -EINVAL;
	}

	return 0;
}

static const struct file_operations cpuregs_fops = {
	.owner		= THIS_MODULE,
	.open		= cpuregs_open,
	.release	= cpuregs_release,
	.ioctl		= cpuregs_ioctl,
};

static struct miscdevice cpuregs_dev = {
	/*
	 * We don't care what minor number we end up with, so tell the
	 * kernel to just pick one.
	 */
	MISC_DYNAMIC_MINOR,
	/*
	 * Name ourselves /dev/cpuregs.
	 */
	"cpuregs",
	/*
	 * What functions to call when a program performs file
	 * operations on the device.
	 */
	&cpuregs_fops
};

static int __init cpuregs_init(void)
{
	int ret;

	/*
	 * Create the "cpuregs" device in the /sys/class/misc directory.
	 * Udev will automatically create the /dev/cpuregs device using
	 * the default rules.
	 */
	ret = misc_register(&cpuregs_dev);
	if (ret)
		printk(KERN_ERR
		       "Unable to register cpuregs misc device\n");

	return ret;
}

module_init(cpuregs_init);

static void __exit
cpuregs_exit(void)
{
	misc_deregister(&cpuregs_dev);
}

module_exit(cpuregs_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mathias Parnaudeau <amigadev@free.fr>");
MODULE_DESCRIPTION("Access PowerPC registers");
MODULE_VERSION("dev");

